import { _decorator, Component, instantiate, math, Node, Prefab, Rect, resources, TextAsset, UITransform, Vec2, Vec3 } from 'cc';
import { BASE_MAP_POS, BASE_SIZE, BLOCK_SIZE, HALF_BLOCK_SIZE, HALF_BULLET_SIZE, HALF_TANK_SIZE, MAP_RES_PATH, MAP_SIZE, TANK_SIZE } from './Const';
import { BlockData, BlockType, Direction } from './Define';
import { myGame } from './MyGame';
import { AudioManager } from './manager/AudioManager';
const { ccclass, property } = _decorator;

@ccclass('Map_Layer')
export class Map_Layer extends Component {
    @property(Node)
    public blocksFront: Node = null

    @property(Node)
    public blocksBehind: Node = null

    @property(Prefab)
    public blockForest: Prefab = null

    @property(Prefab)
    public blockIce: Prefab = null
    
    @property(Prefab)
    public blockRiver: Prefab = null
    
    @property(Prefab)
    public blockStone: Prefab = null
    
    @property(Prefab)
    public blockWall: Prefab = null

    @property(Prefab)
    public blockBase: Prefab = null

    public mapBoundary: Rect = null

    public tankArea: Rect = null

    public blocks: BlockData[][] = []

    onLoad() {
        this.blocks.length = MAP_SIZE.height
        this.blocks = this.blocks.fill(undefined).map((v) => [])
        this.mapBoundary = this.node.getComponent(UITransform).getBoundingBox()
        this.tankArea = new Rect(this.mapBoundary.origin.x + TANK_SIZE.x / 2, this.mapBoundary.origin.y + TANK_SIZE.y / 2, this.mapBoundary.size.width - TANK_SIZE.width, this.mapBoundary.size.height - TANK_SIZE.height)
    }

    onEnable() {
        this.loadMap()
    }

    loadMap() {
        this.blocksBehind.destroyAllChildren()
        this.blocksFront.destroyAllChildren()
        resources.load(MAP_RES_PATH + myGame.level, TextAsset, (err, data: TextAsset) => {
            if (err) {
                console.error(err)
                return
            }

            const originPos = this.node.getComponent(UITransform).getBoundingBox().origin
            const text = data.text
            let index = 0
            for (let i = 0; i < MAP_SIZE.height; i++) {
                this.blocks[i].length = MAP_SIZE.width
                for (let j = 0; j < MAP_SIZE.width; j++) {
                    const blockType = Number(text[index++])
                    let node: Node = null
                    switch(blockType) {
                        case BlockType.None:
                            break
                        case BlockType.Forest:
                            node = instantiate(this.blockForest)
                            break
                        case BlockType.Ice:
                            node = instantiate(this.blockIce)
                            break
                        case BlockType.Wall:
                            node = instantiate(this.blockWall)
                            break
                        case BlockType.River:
                            node = instantiate(this.blockRiver)
                            break
                        case BlockType.Stone:
                            node = instantiate(this.blockStone)
                            break
                    }

                    if (node) {
                        node.parent = blockType == BlockType.Forest ? this.blocksFront: this.blocksBehind
                        node.setPosition(originPos.x + BLOCK_SIZE.width * j + BLOCK_SIZE.width / 2, originPos.y + (MAP_SIZE.height - i) * BLOCK_SIZE.height - BLOCK_SIZE.height / 2)
                        this.blocks[j][i] = {type: blockType, block: node}
                    }
                    else {
                        this.blocks[j][i] = undefined
                    }
                }
            }

            this.genBaseBlock()
        })
    }

    isBulletOutOfBoundary(pos: Vec3) {
        return pos.x < this.mapBoundary.xMin || pos.x > this.mapBoundary.xMax ||
               pos.y > this.mapBoundary.yMax || pos.y < this.mapBoundary.yMin
    }

    tankBoundaryCorrect(pos: Vec3 | Vec2) {
        pos.x = math.clamp(pos.x, this.tankArea.xMin, this.tankArea.xMax)
        pos.y = math.clamp(pos.y, this.tankArea.yMin, this.tankArea.yMax)
    }

    toMapCoord(pos: Vec2, out?: Vec2) {
        out = out || new Vec2()
        const origin = this.mapBoundary.origin
        const col = math.clamp(Math.floor((pos.x - origin.x) / BLOCK_SIZE.width), 0, MAP_SIZE.width - 1)
        const row = math.clamp(MAP_SIZE.height - Math.ceil((pos.y - origin.y) / BLOCK_SIZE.height), 0, MAP_SIZE.height - 1)
        return out.set(col, row)
    }

    convertTankBoundingBoxToMap(pos: Vec2, out?: Rect) {
        out = out || new Rect()
        const offset = 0.5
        const leftTop = new Vec2(pos.x - HALF_TANK_SIZE.width + offset, pos.y + HALF_TANK_SIZE.height - offset)
        const rightBot = new Vec2(pos.x + HALF_TANK_SIZE.width - offset, pos.y - HALF_TANK_SIZE.height + offset)
        this.toMapCoord(leftTop, leftTop)
        this.toMapCoord(rightBot, rightBot)
        return out.set(leftTop.x, leftTop.y, rightBot.x - leftTop.x, rightBot.y - leftTop.y)
    }

    convertBulletBoundingBoxToMap(pos: Vec2|Vec3, out?: Rect) {
        out = out || new Rect()
        const leftTop = new Vec2(pos.x - HALF_BULLET_SIZE.width, pos.y + HALF_BULLET_SIZE.height)
        const rightBot = new Vec2(pos.x + HALF_BULLET_SIZE.width, pos.y - HALF_BULLET_SIZE.height)
        this.toMapCoord(leftTop, leftTop)
        this.toMapCoord(rightBot, rightBot)
        return out.set(leftTop.x, leftTop.y, rightBot.x - leftTop.x, rightBot.y - leftTop.y)
    }

    checkBlockCollision(boundingBox: Rect, dir: Direction, callback?: (type: BlockType, block: Node, col: number, row: number) => boolean) {
        let result = false
        if (dir == Direction.Up) {
            const xStart = boundingBox.xMin
            const xEnd = boundingBox.xMax
            const yStart = boundingBox.yMax
            const yEnd = boundingBox.yMin
            for (let j = yStart; j >= yEnd; j--) {
                for (let i = xStart; i <= xEnd; i++) {
                    const blockData = this.blocks[i][j]
                    if (blockData) {
                        if (callback) {
                            result = callback(blockData.type, blockData.block, i, j)
                        }
                    }

                    if (result)
                        return result
                }
            }
        }
        else if (dir == Direction.Down){
            const xStart = boundingBox.xMin
            const xEnd = boundingBox.xMax
            const yStart = boundingBox.yMin
            const yEnd = boundingBox.yMax
            let result = false
            for (let j = yStart; j <= yEnd; j++) {
                for (let i = xStart; i <= xEnd; i++) {
                    const blockData = this.blocks[i][j]
                    if (blockData) {
                        if (callback) {
                            result = callback(blockData.type, blockData.block, i, j)
                        }
                    }
                    if (result) {
                        return result
                    }
                }
            }
        }
        else if (dir == Direction.Left) {
            const xStart = boundingBox.xMax
            const xEnd = boundingBox.xMin
            const yStart = boundingBox.yMin
            const yEnd = boundingBox.yMax
            let result = false
            for (let i = xStart; i >= xEnd; i--) {
                for (let j = yStart; j <= yEnd; j++) {
                    const blockData = this.blocks[i][j]
                    if (blockData) {
                        if (callback) {
                            result = callback(blockData.type, blockData.block, i, j)
                        }
                    }
                    if (result) {
                        return result
                    }
                }
            }
        }
        else if (dir == Direction.Right) {
            const xStart = boundingBox.xMin
            const xEnd = boundingBox.xMax
            const yStart = boundingBox.yMin
            const yEnd = boundingBox.yMax
            let result = false
            for (let i = xStart; i <= xEnd; i++) {
                for (let j = yStart; j <= yEnd; j++) {
                    const blockData = this.blocks[i][j]
                    if (blockData) {
                        if (callback) {
                            result = callback(blockData.type, blockData.block, i, j)
                        }
                    }
                    if (result) {
                        return result
                    }
                }
            }
        }
    }

    adjustTankPos(pos: Vec2, dir: Direction, block: Node) {
        const blockPos = block.position
        switch(dir) {
            case Direction.Up:
                pos.y = blockPos.y - HALF_BLOCK_SIZE.height - HALF_TANK_SIZE.height
                break
            case Direction.Down:
                pos.y = blockPos.y + HALF_BLOCK_SIZE.height + HALF_TANK_SIZE.height
                break      
            case Direction.Left:
                pos.x = blockPos.x + HALF_BLOCK_SIZE.width + HALF_TANK_SIZE.width
                break
            case Direction.Right:
                pos.x = blockPos.x - HALF_BLOCK_SIZE.width - HALF_TANK_SIZE.width
                break
        }
    }

    destroyBlock(col: number, row: number) {
        const blockData = this.blocks[col][row]
        if (blockData) {
            this.blocks[col][row] = null
            blockData.block.destroy()
        }
        else {
            console.error('格子不存在', col, row)
        }
    }

    genBaseBlock() {
        const blockBase = instantiate(this.blockBase)
        blockBase.parent = this.blocksBehind
        blockBase.setPosition(0, -(MAP_SIZE.height * BLOCK_SIZE.height) / 2 + BASE_SIZE.height / 2)
        BASE_MAP_POS.forEach(pos => {
            this.blocks[pos.x][pos.y] = {type: BlockType.Base, block: blockBase}
        })
    }

    BaseDestroy() {
        const baseBlock = this.blocks[BASE_MAP_POS[0].x][BASE_MAP_POS[0].y]
        if (baseBlock) {
            BASE_MAP_POS.forEach(pos => {
                this.blocks[pos.x][pos.y] = undefined
            })
            baseBlock.block.getChildByName('Normal').active = false
            baseBlock.block.getChildByName('Broken').active = true
        }
        else {
            console.error('基地砖块已销毁')
        }
        AudioManager.inst.playOneShot('camp_bomb')
    }
}


